package lxs.swift.tool.acc;


import org.jetbrains.annotations.NotNull;

import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Function;


public interface Port<I, O> {

    void push(I in);

    O pull();

    void reset();

    default <R> Port<I, R> next(@NotNull Function<O, R> map) {
        Port<I, O> port = this;
        return new Port<I, R>() {

            @Override
            public void push(I in) {
                port.push(in);
            }

            @Override
            public R pull() {
                O o = port.pull();
                return map.apply(o);
            }

            @Override
            public void reset() {
                port.reset();
            }
        };
    }

    /**
     * 将Port转为线程安全的
     */
    default Port<I, O> sync() {
        Port<I, O> port = this;
        return new Port<I, O>() {
            final StampedLock stampedLock = new StampedLock();

            @Override
            public void push(I in) {
                long stamp = stampedLock.writeLock();
                try {
                    port.push(in);
                } finally {
                    stampedLock.unlockWrite(stamp);
                }
            }

            @Override
            public void reset() {
                long stamp = stampedLock.writeLock();
                try {
                    port.reset();
                } finally {
                    stampedLock.unlockWrite(stamp);
                }
            }

            @Override
            public O pull() {
                long stamp = stampedLock.tryOptimisticRead();
                O result = port.pull();
                if (!stampedLock.validate(stamp)) {
                    stamp = stampedLock.readLock();
                    try {
                        result = port.pull();
                    } finally {
                        stampedLock.unlockRead(stamp);
                    }
                }
                return result;
            }
        };
    }

    static <I, M, O> Port<I, O> bind(@NotNull Acc<I, M, O> acc) {
        M m = acc.init();
        AtomicReference<M> state = new AtomicReference<>(m);
        return new Port<I, O>() {
            @Override
            public void push(I in) {
                M m = state.get();
                M newState = acc.input(in, m);
                state.set(newState);
            }

            @Override
            public void reset() {
                M m = state.get();
                M newState = acc.reset(m);
                state.set(newState);
            }

            @Override
            public O pull() {
                M m = state.get();
                return acc.output(m);
            }
        };
    }
}
